package com.linking.third.party.manager;

import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.borealis.common.utils.StringUtils;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.androidpublisher.AndroidPublisher;
import com.google.api.services.androidpublisher.AndroidPublisherScopes;
import com.google.api.services.androidpublisher.model.ProductPurchase;
import com.google.api.services.androidpublisher.model.SubscriptionPurchase;
import com.google.common.collect.Maps;
import com.linking.config.util.FileUtils;
import com.linking.third.party.pojo.bo.GoogleTokenBO;
import com.linking.third.party.pojo.bo.ReceiptVerifyBO;
import com.linking.third.party.pojo.bo.UserVerifyBO;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author YaoWeiXin
 * @Date 2020/2/18 16:31
 * @Description Google管理类
 */
@Slf4j
public class GoogleManager {

  public static UserVerifyBO checkUserToken(String appId, String token) {
    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
        new NetHttpTransport(), JacksonFactory.getDefaultInstance())
        .setAudience(Collections.singletonList(appId)).build();
    try {
      GoogleIdToken idToken = verifier.verify(token);
      return idToken != null ? UserVerifyBO.of(idToken.getPayload()) : UserVerifyBO.ofFail();
    } catch (GeneralSecurityException | IOException e) {
      log.error("GoogleManager checkUserToken error: ", e);
      return UserVerifyBO.ofFail();
    }
  }

  /**
   * 票据验证
   *
   * @param jsonPath    json授权文件路径
   * @param packageName 包名
   * @param productId   产品名
   * @param token       token
   * @return true|false
   */
  public static ReceiptVerifyBO receiptVerify(String jsonPath, String packageName, String productId,
      String token) {
    return receiptVerify(jsonPath, packageName, productId, token, false);
  }

  public static AndroidPublisher getGoogleAndroidPublisher(String jsonPath) throws Exception{
    HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
    String filePath = FileUtils.getFilePath(jsonPath, "/");
    GoogleCredential gcFromJson = GoogleCredential
        .fromStream(new FileInputStream(new File(filePath)), transport,
            JacksonFactory.getDefaultInstance()).createScoped(AndroidPublisherScopes.all());
    GoogleCredential credential = new GoogleCredential.Builder()
        .setTransport(gcFromJson.getTransport())
        .setJsonFactory(gcFromJson.getJsonFactory())
        .setServiceAccountProjectId(gcFromJson.getServiceAccountProjectId())
        .setServiceAccountId(gcFromJson.getServiceAccountId())
        .setServiceAccountUser(gcFromJson.getServiceAccountUser())
        .setServiceAccountScopes(gcFromJson.getServiceAccountScopes())
        .setServiceAccountPrivateKey(gcFromJson.getServiceAccountPrivateKey()).build();
    return new AndroidPublisher.Builder(transport,
        JacksonFactory.getDefaultInstance(), credential).build();
  }


  /**
   * 票据验证
   *
   * @param jsonPath    json授权文件路径
   * @param packageName 包名
   * @param productId   产品名
   * @param token       token
   * @param sendBox     是否沙盒模式
   * @return true|false
   */
  public static ReceiptVerifyBO receiptVerify(String jsonPath, String packageName, String productId,
      String token, Boolean sendBox) {
    return receiptVerify(jsonPath, packageName, productId, token, false, false);
  }

  /**
   * 票据验证
   *
   * @param jsonPath    json授权文件路径
   * @param packageName 包名
   * @param productId   产品名
   * @param token       token
   * @param sendBox     是否沙盒模式
   * @param subscribe   是否订阅
   * @return true|false
   */
  public static ReceiptVerifyBO receiptVerify(String jsonPath, String packageName, String productId,
      String token, Boolean sendBox, Boolean subscribe) {
    if (subscribe != null && subscribe) {
      return receiptVerifySubscribe(jsonPath, packageName, productId, token, sendBox);
    } else {
      return receiptVerifyProduct(jsonPath, packageName, productId, token, sendBox);
    }
  }

  /**
   * 商品票据验证
   *
   * @param jsonPath    json授权文件路径
   * @param packageName 包名
   * @param productId   产品名
   * @param token       token
   * @param sendBox     是否沙盒模式
   * @return true|false
   */
  private static ReceiptVerifyBO receiptVerifyProduct(String jsonPath, String packageName, String productId,
      String token, Boolean sendBox) {
    try {
      AndroidPublisher publisher = getGoogleAndroidPublisher(jsonPath);
      AndroidPublisher.Purchases.Products products = publisher.purchases().products();
      // 参数详细说明: https://developers.google.com/android-publisher/api-ref/purchases/products/get
      AndroidPublisher.Purchases.Products.Get product = products.get(packageName, productId, token);
      // 获取订单信息
      // 返回信息说明: https://developers.google.com/android-publisher/api-ref/purchases/products
      // 通过consumptionState, purchaseState可以判断订单的状态
      ProductPurchase purchase = product.execute();
      log.debug("google-notify===== checkResult:{}", purchase);
      if (purchase.getConsumptionState() != 0) {
        // 已消费
        return ReceiptVerifyBO.ofFail();
      }
      if (purchase.getPurchaseState() != 0) {
        // 未支付
        return ReceiptVerifyBO.ofFail();
      }
      return ReceiptVerifyBO.of(true, purchase.getOrderId(), purchase.getProductId(),
          purchase.getPurchaseType());
    } catch (Exception e) {
      log.info("google receiptVerify failure = {0}", e);
      return ReceiptVerifyBO.ofFail();
    }
  }

  /**
   * 订阅票据验证
   *
   * @param jsonPath    json授权文件路径
   * @param packageName 包名
   * @param productId   产品名
   * @param token       token
   * @param sendBox     是否沙盒模式
   * @return true|false
   */
  private static ReceiptVerifyBO receiptVerifySubscribe(String jsonPath, String packageName, String productId,
      String token, Boolean sendBox) {
    try {
      AndroidPublisher publisher = getGoogleAndroidPublisher(jsonPath);
      AndroidPublisher.Purchases.Subscriptions subscriptions = publisher.purchases()
          .subscriptions();
      AndroidPublisher.Purchases.Subscriptions.Get subscription = subscriptions
          .get(packageName, productId, token);
      SubscriptionPurchase purchase = subscription.execute();
      log.debug("google-notify===== checkResult:{}", purchase);
      long expiryTime = purchase.getExpiryTimeMillis();
      return ReceiptVerifyBO.of(true, purchase.getOrderId(), productId, purchase.getPurchaseType(),
          expiryTime);
    } catch (Exception e) {
      log.info("google receiptVerify failure = {0}", e);
      return ReceiptVerifyBO.ofFail();
    }
  }

  private static Map<String, GoogleTokenBO> cacheToken = Maps.newHashMap();

  private static GoogleTokenBO getAccessToken(String refreshToken, String clientId,
      String clientSecret) {
    Map<String, Object> params = Maps.newHashMap();
    params.put("refresh_token", refreshToken);
    params.put("client_id", clientId);
    params.put("client_secret", clientSecret);
    params.put("grant_type", "refresh_token");
    String result = HttpUtil.post("https://accounts.google.com/o/oauth2/token", params);
    JSONObject jo = JSON.parseObject(result);
    String accessToken = jo.getString("access_token");
    Integer expiresIn = jo.getInteger("expires_in");
    log.debug("包含access_token的JSON信息为: " + jo);
    return GoogleTokenBO.of(accessToken, expiresIn, DateUtil.currentSeconds());
  }

  private static String retrieveAccessToken(String refreshToken, String clientId,
      String clientSecret) {
    GoogleTokenBO googleToken;
    if (cacheToken.containsKey(refreshToken)) {
      googleToken = cacheToken.get(refreshToken);
      Integer expiresIn = googleToken.getExpiresIn();
      Long createTime = googleToken.getCreateTime();
      long nowTime = DateUtil.currentSeconds();
      if (nowTime > (createTime + expiresIn - 300)) {
        // 提前五分钟重新获取access_token
        googleToken = getAccessToken(refreshToken, clientId, clientSecret);
        cacheToken.put(refreshToken, googleToken);
      }
    } else {
      googleToken = getAccessToken(refreshToken, clientId, clientSecret);
      cacheToken.put(refreshToken, googleToken);
    }
    // 2、请求支付信息
    return googleToken.getAccessToken();
  }

  /**
   * 票据验证(秘钥方式
   *
   * @param refreshToken    刷新用token
   * @param clientId        客户端编号
   * @param clientSecret    客户端秘钥
   * @param packageName 包名
   * @param productId   产品名
   * @param token       token
   * @param sendBox     是否沙盒模式
   * @return true|false
   */
  public static ReceiptVerifyBO receiptVerifyBySecret(String refreshToken, String clientId,
        String clientSecret, String packageName, String productId, String token, Boolean sendBox) {
    return receiptVerifyBySecret(refreshToken, clientId, clientSecret, packageName, productId,
        token, sendBox, false);
  }

  /**
   * 票据验证(秘钥方式
   *
   * @param refreshToken    刷新用token
   * @param clientId        客户端编号
   * @param clientSecret    客户端秘钥
   * @param packageName 包名
   * @param productId   产品名
   * @param token       token
   * @param sendBox     是否沙盒模式
   * @return true|false
   */
  public static ReceiptVerifyBO receiptVerifyBySecret(String refreshToken, String clientId,
      String clientSecret, String packageName, String productId, String token, Boolean sendBox,
      Boolean subscribe) {
    try {
      // 1、获取accessToken
      String accessToken = retrieveAccessToken(refreshToken, clientId, clientSecret);
      // 2、请求支付信息
      String url = "https://www.googleapis.com/androidpublisher/v3/applications";
      String getUrl;
      if (subscribe != null && subscribe) {
        url = "https://www.googleapis.com/androidpublisher/v3/applications";
        getUrl = url
            + "/" + packageName
            + "/purchases/subscriptions"
            + "/" + productId
            + "/tokens/" + token
            + "?access_token=" + accessToken;
      } else {
        getUrl = url
            + "/" + packageName
            + "/purchases/products"
            + "/" + productId
            + "/tokens/" + token
            + "?access_token=" + accessToken;
      }
      String result = HttpUtil.get(getUrl);
      if (!JSON.isValid(result)) {
        return ReceiptVerifyBO.ofFail();
      }
      JSONObject jo = JSON.parseObject(result);
      log.debug("google-notify===== checkResult:{}", result);
      long expiryTime = 0;
      if (subscribe != null && subscribe) {
        expiryTime = jo.containsKey("expiryTimeMillis") ? jo.getLong("expiryTimeMillis") : 0L;
      } else {
        String ok = "0";
        if (!ok.equals(jo.getString("consumptionState"))) {
          // 已消费
          return ReceiptVerifyBO.ofFail();
        }
        if (!ok.equals(jo.getString("purchaseState"))) {
          // 未支付
          return ReceiptVerifyBO.ofFail();
        }
      }
      return ReceiptVerifyBO.of(true, jo.getString("orderId"), jo.getString("productId"),
          jo.getInteger("purchaseType"), expiryTime);
    } catch (Exception e) {
      log.info("google receiptVerify failure = {0}", e);
      return ReceiptVerifyBO.ofFail();
    }
  }

  public static Long querySubscribe(String jsonPath, String packageName, String productId,
      String token) {
    try {
      AndroidPublisher publisher = getGoogleAndroidPublisher(jsonPath);
      AndroidPublisher.Purchases.Subscriptions subscriptions = publisher.purchases()
          .subscriptions();
      AndroidPublisher.Purchases.Subscriptions.Get subscription = subscriptions
          .get(packageName, productId, token);
      SubscriptionPurchase subscriptionPurchase = subscription.execute();
      return subscriptionPurchase.getExpiryTimeMillis();
    } catch (GoogleJsonResponseException e) {
      if (StringUtils.isEmpty(e.getMessage()) || !e.getMessage().contains("400 Bad Request")) {
        log.error("google-notify===== Google Response: {}", e.getMessage());
      }
    } catch (Exception e) {
      log.error("google-notify===== error : ", e);
    }
    return -1L;
  }

  public static Long querySubscribeBySecret(String refreshToken, String clientId,
      String clientSecret, String packageName, String productId,
      String token) {
    // 1、获取accessToken
    String accessToken = retrieveAccessToken(refreshToken, clientId, clientSecret);
    // 2、请求支付信息
    String url = "https://www.googleapis.com/androidpublisher/v3/applications";
    String getUrl = url
        + "/" + packageName
        + "/purchases/products"
        + "/" + productId
        + "/tokens/" + token
        + "?access_token=" + accessToken;
    String result = HttpUtil.get(getUrl);
    if (!JSON.isValid(result)) {
      return -1L;
    }
    JSONObject jo = JSON.parseObject(result);
    return jo.getLong("expiryTimeMillis");
  }
}
